green <- miscgis::miscgis_pals$tableau_cat[["green"]]
blue <- miscgis::miscgis_pals$tableau_cat[["blue"]]
orange <- miscgis::miscgis_pals$tableau_cat[["orange"]]
red <- miscgis::miscgis_pals$tableau_cat[["red"]]
teal <- miscgis::miscgis_pals$tableau_cat[["teal"]]
pal_rgb_4 <- miscgis::miscgis_pals$tableau_cat[c("red","gold","green","blue")] %>% unlist %>% palette()
pal_rgb_4 <- miscgis::miscgis_pals$tableau_cat[c("red","gold","green","blue")] %>% unlist %>% palette()
pal_rgb_6 <- miscgis::miscgis_pals$tableau_cat[c("red","gold","green","blue","orange","purple")] %>% unlist %>% palette()
pal_rgb_6 <- miscgis::miscgis_pals$tableau_cat[c("red","gold","green","blue","orange","purple")] %>% unlist %>% palette()
Below is a collection of spatial datasets used in this project.
Seattle Boundary
if(!file.exists(root_file('1-data/4-interim/seattle-sf.rds'))){
tigris::places(state = "WA") %>%
tigris::filter_place(place = "Seattle") %>%
spTransform(CRSobj = crs_proj) %>%
st_as_sf() %>%
miscgis::coerce_to_geom(sf::st_multipolygon) %>%
write_rds(root_file('1-data/4-interim/seattle-sf.rds'))
}
sea_sf <- read_rds(root_file('1-data/4-interim/seattle-sf.rds'))
green <- miscgis::miscgis_pals$tableau_cat[["green"]]
myLfltGrey(data = as(sea_sf,'Spatial')) %>%
myLfltOpts() %>%
addPolygons(color = green,opacity = 1,fillColor = green,fillOpacity = .5)
King County Subdivision Boundary
if(!file.exists(root_file('1-data/4-interim/seattle-ccd-sf.rds'))){
tigris::county_subdivisions(state = "53",county = "033") %>%
subset(NAME == "Seattle") %>%
spTransform(CRSobj = crs_proj) %>%
st_as_sf() %>%
miscgis::coerce_to_geom(sf::st_multipolygon) %>%
write_rds(root_file('1-data/4-interim/seattle-ccd-sf.rds'))
}
sea_ccd_sf <- read_rds(root_file('1-data/4-interim/seattle-ccd-sf.rds'))
myLfltGrey(data = as(sea_ccd_sf,'Spatial')) %>%
myLfltOpts() %>%
addPolygons(color = blue,opacity = 1,fillColor = blue,fillOpacity = .5)
Tracts in King County
Although this assessment is primarily focused on three communities within the Seattle CCD subdivision of King County, one of the indicators (housing market conditions) uses neighboring tracts to determine displacement risk. Some of the neighboring tracts are part of other county subdivision, but rather than targeting just those specific tracts, this method collects data for all King County tracts and then runs the analysis on the appropriate subsets.
In the absence of a straight-forward method for identifying all the census tracts in the Seattle CCD subdivision of King County, it is possible to extract this information from American Factfinder. This tutorial describes how to use the American Factfinder interface to extract a list of all “all tracts within (or partially within) a census place”; substituting “county subdivision” for “place” will retrieve the desired results.
Tract boundaries sometimes change over time, particularly on years when the decennial census is conducted. While this project will primarily use the current set of boundaries, it is necessary to collect the tract boundaries of the 2000-2009 time period so that these datasets can be visually represented in choropleth diagrams.
# 2015 tract boundaries
if(!file.exists(root_file('1-data/4-interim/tr-kc-wtr-2015-sf.rds'))){
# Seattle CCD tracts
# Note: because this selection includes all tracts "within or partially-within" the Seattle CC,
# several tract GEOIDs are duplicated in the selection. For the sake of clarity, these duplicates
# are removed from the final vector of GEOIDs.
tr_ccd_geoid_2012 <-
read_csv(
root_file('1-data/3-external/manual/seattle-ccd/ACS_12_5YR_B01001/ACS_12_5YR_B01001_with_ann.csv'),
col_types = cols(Id2 = col_character()),
skip = 1) %>%
mutate(NEW_GEOID1 = str_sub(Id2,1,5),
NEW_GEOID2 = str_sub(Id2,16,21),
GEOID = paste0(NEW_GEOID1,NEW_GEOID2),
UNIQUE = !duplicated(GEOID)) %>%
filter(UNIQUE) %>%
extract2('GEOID')
# All 2015 KC tracts with Seattle CCD subdivision column
tigris::tracts(state = '53',county = '033', year = 2015) %>%
spTransform(CRSobj = crs_proj) %>%
st_as_sf() %>%
st_cast('MULTIPOLYGON') %>%
mutate(SEACCD_LGL = ifelse(GEOID %in% tr_ccd_geoid_2012,TRUE,FALSE)) %>%
write_rds(root_file('1-data/4-interim/tr-kc-wtr-2015-sf.rds'))
}
# pre-2010 tract boundaries
if(!file.exists(root_file('1-data/4-interim/tr-kc-wtr-2009-sf.rds'))){
# Seattle CCD tracts
# Note: because this selection includes all tracts "within or partially-within" the Seattle CC,
# several tract GEOIDs are duplicated in the selection. For the sake of clarity, these duplicates
# are removed from the final vector of GEOIDs.
tr_ccd_geoid_2009 <-
read_csv(
root_file('1-data/3-external/manual/seattle-ccd/ACS_09_5YR_B01001/ACS_09_5YR_B01001_with_ann.csv'),
col_types = cols(GEO.id2 = col_character())) %>%
mutate(NEW_GEOID1 = str_sub(GEO.id2,1,5),
NEW_GEOID2 = str_sub(GEO.id2,16,21),
GEOID = paste0(NEW_GEOID1,NEW_GEOID2),
UNIQUE = !duplicated(GEOID)) %>%
filter(UNIQUE) %>%
extract2('GEOID')
# All 2015 KC tracts with Seattle CCD subdivision column
my_dl_zip <- function(url,dir_filepath){
temp <- tempfile()
downloader::download(url, dest = temp, mode='wb')
unzip(temp, exdir = dir_filepath)
}
# my_dl_zip('ftp://ftp.census.gov/geo/tiger/TIGER2009/53_WASHINGTON/53033_King_County/tl_2009_53033_tract00.zip',root_file('1-data/3-external/manual/kc-tr/'))
readOGR(dsn = root_file('./1-data/3-external/manual/kc-tr/tl_2009_53033_tract00/'),
layer = 'tl_2009_53033_tract00',
verbose = FALSE,
stringsAsFactors = FALSE) %>%
spTransform(CRSobj = crs_proj) %>%
st_as_sf() %>%
st_cast('MULTIPOLYGON') %>%
mutate(SEACCD_LGL = ifelse(CTIDFP00 %in% tr_ccd_geoid_2009,TRUE,FALSE)) %>%
write_rds(root_file('1-data/4-interim/tr-kc-wtr-2009-sf.rds'))
# Note: the `tigris` package is returning an error for attempts to
# download tract data before 2011
# tigris::tracts(state = '53',county = '033', year = 2009) %>%
# spTransform(CRSobj = crs_proj) %>%
# st_as_sf() %>%
# st_cast('MULTIPOLYGON') %>%
# mutate(SEACCD_LGL = ifelse(GEOID %in% tr_ccd_geoid_2012,TRUE,FALSE)) %>%
# write_rds(root_file('1-data/4-interim/tr-kc-wtr-2009-sf.rds'))
}
tr_kc_wtr_2015_sf <- read_rds(root_file('1-data/4-interim/tr-kc-wtr-2015-sf.rds'))
tr_kc_wtr_2009_sf <- read_rds(root_file('1-data/4-interim/tr-kc-wtr-2009-sf.rds'))
show_tr_ccd_wtr_2009_sf <- function(){
blue_orange <- c(blue,orange) %>% unlist
pal <- colorFactor(blue_orange,levels = c(TRUE,FALSE), ordered = TRUE)
myLfltGrey() %>%
myLfltOpts() %>%
addPolygons(data = as(tr_kc_wtr_2009_sf,"Spatial"),
weight = .5,
color = ~ pal(SEACCD_LGL),
opacity = 1,
fillColor = ~ pal(SEACCD_LGL),
fillOpacity = .5) %>%
addLegend(position = 'bottomright',colors = blue_orange,labels = c('Seattle CCD','Other Tracts'),title = 'pre-2010') %>%
miscgis::styleWidget(style = "float:left;margin:1px")
}
show_tr_ccd_wtr_2015_sf <- function(){
blue_orange <- c(blue,orange) %>% unlist
pal <- colorFactor(blue_orange,levels = c(TRUE,FALSE), ordered = TRUE)
myLfltGrey() %>%
myLfltOpts() %>%
addPolygons(data = as(tr_kc_wtr_2015_sf,"Spatial"),
weight = .5,
color = ~ pal(SEACCD_LGL),
opacity = 1,
fillColor = ~ pal(SEACCD_LGL),
fillOpacity = .5) %>%
addLegend(position = 'bottomright',colors = blue_orange,labels = c('Seattle CCD','Other Tracts'),title = '2010') %>%
miscgis::styleWidget(style = "float:none;margin:1px")
}
show_tr_ccd_wtr_2009_sf()
show_tr_ccd_wtr_2015_sf()
Puget Sound Waterbodies
These are useful for “clipping” census geographies whose boundaries extend into waterbodies.
if(!file.exists(root_file('1-data/4-interim/wtr-sf.rds'))){
fp_wtr <- root_file('1-data/3-external/NHDMajor.gdb')
# check if the file already exists, if not then download it
if(!file.exists(fp_wtr)){
url <- "ftp://www.ecy.wa.gov/gis_a/inlandWaters/NHD/NHDmajor.gdb.zip" # save the URL for the waterbodies data
temp <- tempfile() # create a temporary file to hold the compressed download
download(url, dest = temp, mode="wb") # download the file
unzip (temp, exdir = root_file('1-data/3-external/')) # extract the ESRI geodatabase file to a project folder
}
wtr_sp <-
suppressWarnings(readOGR(dsn = fp_wtr, # create a waterbodies shape
layer = "NHD_MajorWaterbodies",verbose = FALSE,pointDropZ = TRUE)) %>%
gBuffer(byid=TRUE, width=0) %>% # clean up self-intersecting polygons
spTransform(CRSobj = crs_proj) # transform the projection to match the project projection
wtr_sf <-
wtr_sp %>%
st_as_sf() %>%
miscgis::coerce_to_geom(st_multipolygon)
wtr_sf %>%
st_intersects(x = sea_ccd_sf,y = .) %>%
unlist(use.names = F) %>%
wtr_sf[.,] %>%
write_rds(root_file('1-data/4-interim/wtr-sf.rds'))
}
wtr_sf <- read_rds(root_file('1-data/4-interim/wtr-sf.rds'))
show_wtr <- function(){
myLfltGrey(data = as(wtr_sf,'Spatial')) %>%
myLfltOpts() %>%
addPolygons(color = blue, opacity = 1,
weight = .5, fillColor = blue,fillOpacity = .5)
}
show_wtr()
KC Tracts Without (Western) Waterbodies
if(!file.exists(root_file('1-data/4-interim/tr-kc-2015-sf.rds'))){
tr_kc_wtr_2015_sf %>%
filter(TRACTCE %!in% '990100') %>% # remove the Puget Sound tract
mutate(geometry = st_difference(geometry,st_union(wtr_sf))) %>%
coerce_to_geom(st_multipolygon) %>%
write_rds(root_file('1-data/4-interim/tr-kc-2015-sf.rds'))
}
if(!file.exists(root_file('1-data/4-interim/tr-kc-2009-sf.rds'))){
tr_kc_wtr_2009_sf %>%
filter(TRACTCE00 %!in% '990100') %>% # remove the Puget Sound tract
mutate(geometry = st_difference(geometry,st_union(wtr_sf))) %>%
coerce_to_geom(st_multipolygon) %>%
write_rds(root_file('1-data/4-interim/tr-kc-2009-sf.rds'))
}
tr_kc_2015_sf <- read_rds(root_file('1-data/4-interim/tr-kc-2015-sf.rds'))
tr_kc_2009_sf <- read_rds(root_file('1-data/4-interim/tr-kc-2009-sf.rds'))
show_tr_kc_2009_sf <- function(){
blue_orange <- c(blue,orange) %>% unlist
pal <- colorFactor(blue_orange,levels = c(TRUE,FALSE), ordered = TRUE)
myLfltGrey() %>%
myLfltOpts() %>%
addPolygons(data = as(tr_kc_2009_sf,"Spatial"),
weight = .5,
color = ~ pal(SEACCD_LGL),
opacity = 1,
fillColor = ~ pal(SEACCD_LGL),
fillOpacity = .5) %>%
addLegend(position = 'bottomright',colors = blue_orange,labels = c('Seattle CCD','Other Tracts'),title = 'pre-2010') %>%
miscgis::styleWidget(style = "float:left;margin:1px")
}
show_tr_kc_2015_sf <- function(){
blue_orange <- c(blue,orange) %>% unlist
pal <- colorFactor(blue_orange,levels = c(TRUE,FALSE), ordered = TRUE)
myLfltGrey() %>%
myLfltOpts() %>%
addPolygons(data = as(tr_kc_2015_sf,"Spatial"),
weight = .5,
color = ~ pal(SEACCD_LGL),
opacity = 1,
fillColor = ~ pal(SEACCD_LGL),
fillOpacity = .5) %>%
addLegend(position = 'bottomright',colors = blue_orange,labels = c('Seattle CCD','Other Tracts'),title = '2010') %>%
miscgis::styleWidget(style = "float:none;margin:1px")
}
show_tr_kc_2009_sf()
show_tr_kc_2015_sf()
LS0tCmRmX3ByaW50OiB0aWJibGUKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICBwZGZfZG9jdW1lbnQ6CiAgICBrZWVwX3RleDogeWVzCmFsd2F5c19hbGxvd19odG1sOiB5ZXMKLS0tCgpgYGB7ciBtaXNjLXNldHVwLCBlY2hvID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxjb21tZW50PUZBTFNFfQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KG9wZXJhdG9yLnRvb2xzKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHJwcm9qcm9vdCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocmdkYWwpCmxpYnJhcnkoc3ApCmxpYnJhcnkocmdlb3MpCmxpYnJhcnkobWlzY2dpcykKbGlicmFyeSh0aWdyaXMpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRvd25sb2FkZXIpCmxpYnJhcnkobWlzY2dpcykKbGlicmFyeShzZikKcm9vdCA8LSBycHJvanJvb3Q6OmlzX3JzdHVkaW9fcHJvamVjdApyb290X2ZpbGUgPC0gcm9vdCRtYWtlX2ZpeF9maWxlKCkKYGBgCgpgYGB7ciBtaXNjLWNvbG9yc30KZ3JlZW4gPC0gbWlzY2dpczo6bWlzY2dpc19wYWxzJHRhYmxlYXVfY2F0W1siZ3JlZW4iXV0KYmx1ZSA8LSBtaXNjZ2lzOjptaXNjZ2lzX3BhbHMkdGFibGVhdV9jYXRbWyJibHVlIl1dCm9yYW5nZSA8LSBtaXNjZ2lzOjptaXNjZ2lzX3BhbHMkdGFibGVhdV9jYXRbWyJvcmFuZ2UiXV0KcmVkIDwtIG1pc2NnaXM6Om1pc2NnaXNfcGFscyR0YWJsZWF1X2NhdFtbInJlZCJdXQp0ZWFsIDwtIG1pc2NnaXM6Om1pc2NnaXNfcGFscyR0YWJsZWF1X2NhdFtbInRlYWwiXV0KcGFsX3JnYl80IDwtIG1pc2NnaXM6Om1pc2NnaXNfcGFscyR0YWJsZWF1X2NhdFtjKCJyZWQiLCJnb2xkIiwiZ3JlZW4iLCJibHVlIildICU+JSB1bmxpc3QgJT4lIHBhbGV0dGUoKQpwYWxfcmdiXzQgPC0gbWlzY2dpczo6bWlzY2dpc19wYWxzJHRhYmxlYXVfY2F0W2MoInJlZCIsImdvbGQiLCJncmVlbiIsImJsdWUiKV0gJT4lIHVubGlzdCAlPiUgcGFsZXR0ZSgpCnBhbF9yZ2JfNiA8LSBtaXNjZ2lzOjptaXNjZ2lzX3BhbHMkdGFibGVhdV9jYXRbYygicmVkIiwiZ29sZCIsImdyZWVuIiwiYmx1ZSIsIm9yYW5nZSIsInB1cnBsZSIpXSAlPiUgdW5saXN0ICU+JSBwYWxldHRlKCkKcGFsX3JnYl82IDwtIG1pc2NnaXM6Om1pc2NnaXNfcGFscyR0YWJsZWF1X2NhdFtjKCJyZWQiLCJnb2xkIiwiZ3JlZW4iLCJibHVlIiwib3JhbmdlIiwicHVycGxlIildICU+JSB1bmxpc3QgJT4lIHBhbGV0dGUoKQpgYGAKCkJlbG93IGlzIGEgY29sbGVjdGlvbiBvZiBzcGF0aWFsIGRhdGFzZXRzIHVzZWQgaW4gdGhpcyBwcm9qZWN0LgoKIyMjIFNlYXR0bGUgQm91bmRhcnkgey19CgpgYGB7ciBtaXNjLXNlYS1ib3VuZCwgZmlnLmNhcD0iU2VhdHRsZVwncyBnZW9ncmFwaGljIGJvdW5kYXJ5In0KCmlmKCFmaWxlLmV4aXN0cyhyb290X2ZpbGUoJzEtZGF0YS80LWludGVyaW0vc2VhdHRsZS1zZi5yZHMnKSkpewogICAgICAgIHRpZ3Jpczo6cGxhY2VzKHN0YXRlID0gIldBIikgJT4lCiAgICAgICAgICAgICAgICB0aWdyaXM6OmZpbHRlcl9wbGFjZShwbGFjZSA9ICJTZWF0dGxlIikgJT4lCiAgICAgICAgICAgICAgICBzcFRyYW5zZm9ybShDUlNvYmogPSBjcnNfcHJvaikgJT4lIAogICAgICAgICAgICAgICAgc3RfYXNfc2YoKSAlPiUgCiAgICAgICAgICAgICAgICBtaXNjZ2lzOjpjb2VyY2VfdG9fZ2VvbShzZjo6c3RfbXVsdGlwb2x5Z29uKSAlPiUgCiAgICAgICAgICAgICAgICB3cml0ZV9yZHMocm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL3NlYXR0bGUtc2YucmRzJykpCiAgICAgICAgCn0KCnNlYV9zZiA8LSByZWFkX3Jkcyhyb290X2ZpbGUoJzEtZGF0YS80LWludGVyaW0vc2VhdHRsZS1zZi5yZHMnKSkKCmdyZWVuIDwtIG1pc2NnaXM6Om1pc2NnaXNfcGFscyR0YWJsZWF1X2NhdFtbImdyZWVuIl1dCgpteUxmbHRHcmV5KGRhdGEgPSBhcyhzZWFfc2YsJ1NwYXRpYWwnKSkgJT4lIAogICAgICAgIG15TGZsdE9wdHMoKSAlPiUgCiAgICAgICAgYWRkUG9seWdvbnMoY29sb3IgPSBncmVlbixvcGFjaXR5ID0gMSxmaWxsQ29sb3IgPSBncmVlbixmaWxsT3BhY2l0eSA9IC41KQoKYGBgCgojIyMgS2luZyBDb3VudHkgU3ViZGl2aXNpb24gQm91bmRhcnkgey19CgpgYGB7ciBtaXNjLXNlYS1jY2QsIGZpZy5jYXA9IlNlYXR0bGUgU3ViZGl2aXNpb24gb2YgS2luZyBDb3VudHlcJ3MgZ2VvZ3JhcGhpYyBib3VuZGFyeSJ9CgppZighZmlsZS5leGlzdHMocm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL3NlYXR0bGUtY2NkLXNmLnJkcycpKSl7CiAgICAgICAgdGlncmlzOjpjb3VudHlfc3ViZGl2aXNpb25zKHN0YXRlID0gIjUzIixjb3VudHkgPSAiMDMzIikgJT4lIAogICAgICAgICAgICAgICAgc3Vic2V0KE5BTUUgPT0gIlNlYXR0bGUiKSAlPiUgCiAgICAgICAgICAgICAgICBzcFRyYW5zZm9ybShDUlNvYmogPSBjcnNfcHJvaikgJT4lIAogICAgICAgICAgICAgICAgc3RfYXNfc2YoKSAlPiUgCiAgICAgICAgICAgICAgICBtaXNjZ2lzOjpjb2VyY2VfdG9fZ2VvbShzZjo6c3RfbXVsdGlwb2x5Z29uKSAlPiUgCiAgICAgICAgICAgICAgICB3cml0ZV9yZHMocm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL3NlYXR0bGUtY2NkLXNmLnJkcycpKQp9CgpzZWFfY2NkX3NmIDwtIHJlYWRfcmRzKHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS9zZWF0dGxlLWNjZC1zZi5yZHMnKSkKCm15TGZsdEdyZXkoZGF0YSA9IGFzKHNlYV9jY2Rfc2YsJ1NwYXRpYWwnKSkgJT4lIAogICAgICAgIG15TGZsdE9wdHMoKSAlPiUgCiAgICAgICAgYWRkUG9seWdvbnMoY29sb3IgPSBibHVlLG9wYWNpdHkgPSAxLGZpbGxDb2xvciA9IGJsdWUsZmlsbE9wYWNpdHkgPSAuNSkKCmBgYAoKIyMjIFRyYWN0cyBpbiBLaW5nIENvdW50eSB7LX0KQWx0aG91Z2ggdGhpcyBhc3Nlc3NtZW50IGlzIHByaW1hcmlseSBmb2N1c2VkIG9uIHRocmVlIGNvbW11bml0aWVzIHdpdGhpbiB0aGUgU2VhdHRsZSBDQ0Qgc3ViZGl2aXNpb24gb2YgS2luZyBDb3VudHksIG9uZSBvZiB0aGUgaW5kaWNhdG9ycyAoaG91c2luZyBtYXJrZXQgY29uZGl0aW9ucykgdXNlcyBuZWlnaGJvcmluZyB0cmFjdHMgdG8gZGV0ZXJtaW5lIGRpc3BsYWNlbWVudCByaXNrLiBTb21lIG9mIHRoZSBuZWlnaGJvcmluZyB0cmFjdHMgYXJlIHBhcnQgb2Ygb3RoZXIgY291bnR5IHN1YmRpdmlzaW9uLCBidXQgcmF0aGVyIHRoYW4gdGFyZ2V0aW5nIGp1c3QgdGhvc2Ugc3BlY2lmaWMgdHJhY3RzLCB0aGlzIG1ldGhvZCBjb2xsZWN0cyBkYXRhIGZvciBhbGwgS2luZyBDb3VudHkgdHJhY3RzIGFuZCB0aGVuIHJ1bnMgdGhlIGFuYWx5c2lzIG9uIHRoZSBhcHByb3ByaWF0ZSBzdWJzZXRzLgoKSW4gdGhlIGFic2VuY2Ugb2YgYSBzdHJhaWdodC1mb3J3YXJkIG1ldGhvZCBmb3IgaWRlbnRpZnlpbmcgYWxsIHRoZSBjZW5zdXMgdHJhY3RzIGluIHRoZSBTZWF0dGxlIENDRCBzdWJkaXZpc2lvbiBvZiBLaW5nIENvdW50eSwgaXQgaXMgcG9zc2libGUgdG8gZXh0cmFjdCB0aGlzIGluZm9ybWF0aW9uIGZyb20gW0FtZXJpY2FuIEZhY3RmaW5kZXJdKGh0dHBzOi8vZmFjdGZpbmRlci5jZW5zdXMuZ292L2ZhY2VzL25hdi9qc2YvcGFnZXMvaW5kZXgueGh0bWwpLiBUaGlzIFt0dXRvcmlhbF0oaHR0cHM6Ly9hc2suY2Vuc3VzLmdvdi9wcndlYi9QUlNlcnZsZXRDdXN0b20/cHlBY3Rpdml0eT1weU1vYmlsZVNuYXBTdGFydCZBY3Rpb249c2hvd0hhcm5lc3MmUHVycG9zZT1LTUhlbHBTaXRlUG9ydGFsJmNsYXNzTmFtZT1EYXRhLVBvcnRhbCZSZWFkT25seT10cnVlJnB6TW9iaWxlSW5pdEFjdGl2aXR5PUtNRmV0Y2hBcnRpY2xlRXh0ZXJuYWwmcHpNb2JpbGVJbml0QWN0aXZpdHlQYXJhbXM9JTI2QXJ0aWNsZUlEJTNES0NQLTMyNTYmcHpNb2JpbGVDb250ZXh0UGFnZU5hbWU9cHlEaXNwbGF5SGFybmVzcykgZGVzY3JpYmVzIGhvdyB0byB1c2UgdGhlIEFtZXJpY2FuIEZhY3RmaW5kZXIgaW50ZXJmYWNlIHRvIGV4dHJhY3QgYSBsaXN0IG9mIGFsbCAiYWxsIHRyYWN0cyB3aXRoaW4gKG9yIHBhcnRpYWxseSB3aXRoaW4pIGEgY2Vuc3VzIHBsYWNlIjsgc3Vic3RpdHV0aW5nICJjb3VudHkgc3ViZGl2aXNpb24iIGZvciAicGxhY2UiIHdpbGwgcmV0cmlldmUgdGhlIGRlc2lyZWQgcmVzdWx0cy4KClRyYWN0IGJvdW5kYXJpZXMgc29tZXRpbWVzIGNoYW5nZSBvdmVyIHRpbWUsIHBhcnRpY3VsYXJseSBvbiB5ZWFycyB3aGVuIHRoZSBkZWNlbm5pYWwgY2Vuc3VzIGlzIGNvbmR1Y3RlZC4gV2hpbGUgdGhpcyBwcm9qZWN0IHdpbGwgcHJpbWFyaWx5IHVzZSB0aGUgY3VycmVudCBzZXQgb2YgYm91bmRhcmllcywgaXQgaXMgbmVjZXNzYXJ5IHRvIGNvbGxlY3QgdGhlIHRyYWN0IGJvdW5kYXJpZXMgb2YgdGhlIDIwMDAtMjAwOSB0aW1lIHBlcmlvZCBzbyB0aGF0IHRoZXNlIGRhdGFzZXRzIGNhbiBiZSB2aXN1YWxseSByZXByZXNlbnRlZCBpbiBjaG9yb3BsZXRoIGRpYWdyYW1zLgoKYGBge3IgbWlzYy10ci1rYy13dHIsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTIsIGZpZy5zaG93PSdob2xkJywgZHBpPTE1MH0KIyAyMDE1IHRyYWN0IGJvdW5kYXJpZXMKaWYoIWZpbGUuZXhpc3RzKHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS90ci1rYy13dHItMjAxNS1zZi5yZHMnKSkpewogICAgICAgIAogICAgICAgICMgU2VhdHRsZSBDQ0QgdHJhY3RzIAogICAgICAgICMgTm90ZTogYmVjYXVzZSB0aGlzIHNlbGVjdGlvbiBpbmNsdWRlcyBhbGwgdHJhY3RzICJ3aXRoaW4gb3IgcGFydGlhbGx5LXdpdGhpbiIgdGhlIFNlYXR0bGUgQ0MsCiAgICAgICAgIyBzZXZlcmFsIHRyYWN0IEdFT0lEcyBhcmUgZHVwbGljYXRlZCBpbiB0aGUgc2VsZWN0aW9uLiBGb3IgdGhlIHNha2Ugb2YgY2xhcml0eSwgdGhlc2UgZHVwbGljYXRlcwogICAgICAgICMgYXJlIHJlbW92ZWQgZnJvbSB0aGUgZmluYWwgdmVjdG9yIG9mIEdFT0lEcy4KICAgICAgICB0cl9jY2RfZ2VvaWRfMjAxMiA8LSAKICAgICAgICAgICAgICAgIHJlYWRfY3N2KAogICAgICAgICAgICAgICAgICAgICAgICByb290X2ZpbGUoJzEtZGF0YS8zLWV4dGVybmFsL21hbnVhbC9zZWF0dGxlLWNjZC9BQ1NfMTJfNVlSX0IwMTAwMS9BQ1NfMTJfNVlSX0IwMTAwMV93aXRoX2Fubi5jc3YnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoSWQyID0gY29sX2NoYXJhY3RlcigpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHNraXAgPSAxKSAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUoTkVXX0dFT0lEMSA9IHN0cl9zdWIoSWQyLDEsNSksCiAgICAgICAgICAgICAgICAgICAgICAgTkVXX0dFT0lEMiA9IHN0cl9zdWIoSWQyLDE2LDIxKSwKICAgICAgICAgICAgICAgICAgICAgICBHRU9JRCA9IHBhc3RlMChORVdfR0VPSUQxLE5FV19HRU9JRDIpLAogICAgICAgICAgICAgICAgICAgICAgIFVOSVFVRSA9ICFkdXBsaWNhdGVkKEdFT0lEKSkgJT4lCiAgICAgICAgICAgICAgICBmaWx0ZXIoVU5JUVVFKSAlPiUgCiAgICAgICAgICAgICAgICBleHRyYWN0MignR0VPSUQnKQogICAgICAgIAogICAgICAgIAogICAgICAgICMgQWxsIDIwMTUgS0MgdHJhY3RzIHdpdGggU2VhdHRsZSBDQ0Qgc3ViZGl2aXNpb24gY29sdW1uCiAgICAgICAgCiAgICAgICAgdGlncmlzOjp0cmFjdHMoc3RhdGUgPSAnNTMnLGNvdW50eSA9ICcwMzMnLCB5ZWFyID0gMjAxNSkgJT4lCiAgICAgICAgICAgICAgICBzcFRyYW5zZm9ybShDUlNvYmogPSBjcnNfcHJvaikgJT4lIAogICAgICAgICAgICAgICAgc3RfYXNfc2YoKSAlPiUgCiAgICAgICAgICAgICAgICBzdF9jYXN0KCdNVUxUSVBPTFlHT04nKSAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUoU0VBQ0NEX0xHTCA9IGlmZWxzZShHRU9JRCAlaW4lIHRyX2NjZF9nZW9pZF8yMDEyLFRSVUUsRkFMU0UpKSAlPiUgCiAgICAgICAgICAgICAgICB3cml0ZV9yZHMocm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL3RyLWtjLXd0ci0yMDE1LXNmLnJkcycpKQogICAgICAgICAgICAgIAp9CgojIHByZS0yMDEwIHRyYWN0IGJvdW5kYXJpZXMKaWYoIWZpbGUuZXhpc3RzKHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS90ci1rYy13dHItMjAwOS1zZi5yZHMnKSkpewogICAgICAgIAogICAgICAgICMgU2VhdHRsZSBDQ0QgdHJhY3RzIAogICAgICAgICMgTm90ZTogYmVjYXVzZSB0aGlzIHNlbGVjdGlvbiBpbmNsdWRlcyBhbGwgdHJhY3RzICJ3aXRoaW4gb3IgcGFydGlhbGx5LXdpdGhpbiIgdGhlIFNlYXR0bGUgQ0MsCiAgICAgICAgIyBzZXZlcmFsIHRyYWN0IEdFT0lEcyBhcmUgZHVwbGljYXRlZCBpbiB0aGUgc2VsZWN0aW9uLiBGb3IgdGhlIHNha2Ugb2YgY2xhcml0eSwgdGhlc2UgZHVwbGljYXRlcwogICAgICAgICMgYXJlIHJlbW92ZWQgZnJvbSB0aGUgZmluYWwgdmVjdG9yIG9mIEdFT0lEcy4KICAgICAgICB0cl9jY2RfZ2VvaWRfMjAwOSA8LSAKICAgICAgICAgICAgICAgIHJlYWRfY3N2KAogICAgICAgICAgICAgICAgICAgICAgICByb290X2ZpbGUoJzEtZGF0YS8zLWV4dGVybmFsL21hbnVhbC9zZWF0dGxlLWNjZC9BQ1NfMDlfNVlSX0IwMTAwMS9BQ1NfMDlfNVlSX0IwMTAwMV93aXRoX2Fubi5jc3YnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoR0VPLmlkMiA9IGNvbF9jaGFyYWN0ZXIoKSkpICU+JSAKICAgICAgICAgICAgICAgIG11dGF0ZShORVdfR0VPSUQxID0gc3RyX3N1YihHRU8uaWQyLDEsNSksCiAgICAgICAgICAgICAgICAgICAgICAgTkVXX0dFT0lEMiA9IHN0cl9zdWIoR0VPLmlkMiwxNiwyMSksCiAgICAgICAgICAgICAgICAgICAgICAgR0VPSUQgPSBwYXN0ZTAoTkVXX0dFT0lEMSxORVdfR0VPSUQyKSwKICAgICAgICAgICAgICAgICAgICAgICBVTklRVUUgPSAhZHVwbGljYXRlZChHRU9JRCkpICU+JQogICAgICAgICAgICAgICAgZmlsdGVyKFVOSVFVRSkgJT4lIAogICAgICAgICAgICAgICAgZXh0cmFjdDIoJ0dFT0lEJykKICAgICAgICAKICAgICAgICAKICAgICAgICAjIEFsbCAyMDE1IEtDIHRyYWN0cyB3aXRoIFNlYXR0bGUgQ0NEIHN1YmRpdmlzaW9uIGNvbHVtbgogICAgICAgIAogICAgICAgIG15X2RsX3ppcCA8LSBmdW5jdGlvbih1cmwsZGlyX2ZpbGVwYXRoKXsKICAgICAgICAgICAgICAgIHRlbXAgPC0gdGVtcGZpbGUoKQogICAgICAgICAgICAgICAgZG93bmxvYWRlcjo6ZG93bmxvYWQodXJsLCBkZXN0ID0gdGVtcCwgbW9kZT0nd2InKQogICAgICAgICAgICAgICAgdW56aXAodGVtcCwgZXhkaXIgPSBkaXJfZmlsZXBhdGgpCiAgICAgICAgICAgICAgICAKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgIyBteV9kbF96aXAoJ2Z0cDovL2Z0cC5jZW5zdXMuZ292L2dlby90aWdlci9USUdFUjIwMDkvNTNfV0FTSElOR1RPTi81MzAzM19LaW5nX0NvdW50eS90bF8yMDA5XzUzMDMzX3RyYWN0MDAuemlwJyxyb290X2ZpbGUoJzEtZGF0YS8zLWV4dGVybmFsL21hbnVhbC9rYy10ci8nKSkKCgogICAgICAgIHJlYWRPR1IoZHNuID0gcm9vdF9maWxlKCcuLzEtZGF0YS8zLWV4dGVybmFsL21hbnVhbC9rYy10ci90bF8yMDA5XzUzMDMzX3RyYWN0MDAvJyksCiAgICAgICAgICAgICAgICBsYXllciA9ICd0bF8yMDA5XzUzMDMzX3RyYWN0MDAnLAogICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUgCiAgICAgICAgICAgICAgICBzcFRyYW5zZm9ybShDUlNvYmogPSBjcnNfcHJvaikgJT4lCiAgICAgICAgICAgICAgICBzdF9hc19zZigpICU+JQogICAgICAgICAgICAgICAgc3RfY2FzdCgnTVVMVElQT0xZR09OJykgJT4lIAogICAgICAgICAgICAgICAgbXV0YXRlKFNFQUNDRF9MR0wgPSBpZmVsc2UoQ1RJREZQMDAgJWluJSB0cl9jY2RfZ2VvaWRfMjAwOSxUUlVFLEZBTFNFKSkgJT4lIAogICAgICAgICAgICAgICAgd3JpdGVfcmRzKHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS90ci1rYy13dHItMjAwOS1zZi5yZHMnKSkKICAgICAgICAgICAgICAgIAogICAgICAgIAogICAgICAgICMgTm90ZTogdGhlIGB0aWdyaXNgIHBhY2thZ2UgaXMgcmV0dXJuaW5nIGFuIGVycm9yIGZvciBhdHRlbXB0cyB0bwogICAgICAgICMgZG93bmxvYWQgdHJhY3QgZGF0YSBiZWZvcmUgMjAxMQogICAgICAgICMgdGlncmlzOjp0cmFjdHMoc3RhdGUgPSAnNTMnLGNvdW50eSA9ICcwMzMnLCB5ZWFyID0gMjAwOSkgJT4lCiAgICAgICAgIyAgICAgICAgIHNwVHJhbnNmb3JtKENSU29iaiA9IGNyc19wcm9qKSAlPiUKICAgICAgICAjICAgICAgICAgc3RfYXNfc2YoKSAlPiUKICAgICAgICAjICAgICAgICAgc3RfY2FzdCgnTVVMVElQT0xZR09OJykgJT4lCiAgICAgICAgIyAgICAgICAgIG11dGF0ZShTRUFDQ0RfTEdMID0gaWZlbHNlKEdFT0lEICVpbiUgdHJfY2NkX2dlb2lkXzIwMTIsVFJVRSxGQUxTRSkpICU+JQogICAgICAgICMgICAgICAgICB3cml0ZV9yZHMocm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL3RyLWtjLXd0ci0yMDA5LXNmLnJkcycpKQogICAgICAgIAp9CgoKdHJfa2Nfd3RyXzIwMTVfc2YgPC0gcmVhZF9yZHMocm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL3RyLWtjLXd0ci0yMDE1LXNmLnJkcycpKQoKdHJfa2Nfd3RyXzIwMDlfc2YgPC0gcmVhZF9yZHMocm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL3RyLWtjLXd0ci0yMDA5LXNmLnJkcycpKQoKc2hvd190cl9jY2Rfd3RyXzIwMDlfc2YgPC0gZnVuY3Rpb24oKXsKICAgICAgICAKICAgICAgICBibHVlX29yYW5nZSA8LSBjKGJsdWUsb3JhbmdlKSAlPiUgdW5saXN0CiAgICAgICAgCiAgICAgICAgcGFsIDwtIGNvbG9yRmFjdG9yKGJsdWVfb3JhbmdlLGxldmVscyA9IGMoVFJVRSxGQUxTRSksIG9yZGVyZWQgPSBUUlVFKQogICAgICAgIAogICAgICAgIG15TGZsdEdyZXkoKSAlPiUgCiAgICAgICAgICAgICAgICBteUxmbHRPcHRzKCkgJT4lIAogICAgICAgICAgICAgICAgYWRkUG9seWdvbnMoZGF0YSA9IGFzKHRyX2tjX3d0cl8yMDA5X3NmLCJTcGF0aWFsIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfiBwYWwoU0VBQ0NEX0xHTCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcGFjaXR5ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxDb2xvciA9IH4gcGFsKFNFQUNDRF9MR0wpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAuNSkgJT4lIAogICAgICAgICAgICAgICAgYWRkTGVnZW5kKHBvc2l0aW9uID0gJ2JvdHRvbXJpZ2h0Jyxjb2xvcnMgPSBibHVlX29yYW5nZSxsYWJlbHMgPSBjKCdTZWF0dGxlIENDRCcsJ090aGVyIFRyYWN0cycpLHRpdGxlID0gJ3ByZS0yMDEwJykgJT4lIAogICAgICAgICAgICAgICAgbWlzY2dpczo6c3R5bGVXaWRnZXQoc3R5bGUgPSAiZmxvYXQ6bGVmdDttYXJnaW46MXB4IikKfQpzaG93X3RyX2NjZF93dHJfMjAxNV9zZiA8LSBmdW5jdGlvbigpewogICAgICAgIAogICAgICAgIGJsdWVfb3JhbmdlIDwtIGMoYmx1ZSxvcmFuZ2UpICU+JSB1bmxpc3QKICAgICAgICAKICAgICAgICBwYWwgPC0gY29sb3JGYWN0b3IoYmx1ZV9vcmFuZ2UsbGV2ZWxzID0gYyhUUlVFLEZBTFNFKSwgb3JkZXJlZCA9IFRSVUUpCiAgICAgICAgCiAgICAgICAgbXlMZmx0R3JleSgpICU+JSAKICAgICAgICAgICAgICAgIG15TGZsdE9wdHMoKSAlPiUgCiAgICAgICAgICAgICAgICBhZGRQb2x5Z29ucyhkYXRhID0gYXModHJfa2Nfd3RyXzIwMTVfc2YsIlNwYXRpYWwiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+IHBhbChTRUFDQ0RfTEdMKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbENvbG9yID0gfiBwYWwoU0VBQ0NEX0xHTCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IC41KSAlPiUgCiAgICAgICAgICAgICAgICBhZGRMZWdlbmQocG9zaXRpb24gPSAnYm90dG9tcmlnaHQnLGNvbG9ycyA9IGJsdWVfb3JhbmdlLGxhYmVscyA9IGMoJ1NlYXR0bGUgQ0NEJywnT3RoZXIgVHJhY3RzJyksdGl0bGUgPSAnMjAxMCcpICU+JSAKICAgICAgICAgICAgICAgIG1pc2NnaXM6OnN0eWxlV2lkZ2V0KHN0eWxlID0gImZsb2F0Om5vbmU7bWFyZ2luOjFweCIpCn0KCgpzaG93X3RyX2NjZF93dHJfMjAwOV9zZigpCnNob3dfdHJfY2NkX3d0cl8yMDE1X3NmKCkKCgpgYGAKCiMjIyBQdWdldCBTb3VuZCBXYXRlcmJvZGllcyB7LX0KClRoZXNlIGFyZSB1c2VmdWwgZm9yICJjbGlwcGluZyIgY2Vuc3VzIGdlb2dyYXBoaWVzIHdob3NlIGJvdW5kYXJpZXMgZXh0ZW5kIGludG8gd2F0ZXJib2RpZXMuCgpgYGB7ciBtaXNjLXd0ciwgZmlnLmNhcD0iUHVnZXQgU291bmQgd2F0ZXJib2RpZXMifQoKaWYoIWZpbGUuZXhpc3RzKHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS93dHItc2YucmRzJykpKXsKICAgICAgICBmcF93dHIgPC0gcm9vdF9maWxlKCcxLWRhdGEvMy1leHRlcm5hbC9OSERNYWpvci5nZGInKQoKIyBjaGVjayBpZiB0aGUgZmlsZSBhbHJlYWR5IGV4aXN0cywgaWYgbm90IHRoZW4gZG93bmxvYWQgaXQKaWYoIWZpbGUuZXhpc3RzKGZwX3d0cikpewogICAgICAgIAogICAgICAgIHVybCA8LSAiZnRwOi8vd3d3LmVjeS53YS5nb3YvZ2lzX2EvaW5sYW5kV2F0ZXJzL05IRC9OSERtYWpvci5nZGIuemlwIiAjIHNhdmUgdGhlIFVSTCBmb3IgdGhlIHdhdGVyYm9kaWVzIGRhdGEKICAgICAgICAKICAgICAgICB0ZW1wIDwtIHRlbXBmaWxlKCkgIyBjcmVhdGUgYSB0ZW1wb3JhcnkgZmlsZSB0byBob2xkIHRoZSBjb21wcmVzc2VkIGRvd25sb2FkCiAgICAgICAgCiAgICAgICAgZG93bmxvYWQodXJsLCBkZXN0ID0gdGVtcCwgbW9kZT0id2IiKSAjIGRvd25sb2FkIHRoZSBmaWxlCiAgICAgICAgCiAgICAgICAgdW56aXAgKHRlbXAsIGV4ZGlyID0gcm9vdF9maWxlKCcxLWRhdGEvMy1leHRlcm5hbC8nKSkgIyBleHRyYWN0IHRoZSBFU1JJIGdlb2RhdGFiYXNlIGZpbGUgdG8gYSBwcm9qZWN0IGZvbGRlcgp9Cgp3dHJfc3AgPC0KICAgICAgICBzdXBwcmVzc1dhcm5pbmdzKHJlYWRPR1IoZHNuID0gZnBfd3RyLCAgICAgICMgY3JlYXRlIGEgd2F0ZXJib2RpZXMgc2hhcGUKICAgICAgICAgICAgICAgIGxheWVyID0gIk5IRF9NYWpvcldhdGVyYm9kaWVzIix2ZXJib3NlID0gRkFMU0UscG9pbnREcm9wWiA9IFRSVUUpKSAlPiUKICAgICAgICBnQnVmZmVyKGJ5aWQ9VFJVRSwgd2lkdGg9MCkgJT4lICMgY2xlYW4gdXAgc2VsZi1pbnRlcnNlY3RpbmcgcG9seWdvbnMKICAgICAgICBzcFRyYW5zZm9ybShDUlNvYmogPSBjcnNfcHJvaikgICMgdHJhbnNmb3JtIHRoZSBwcm9qZWN0aW9uIHRvIG1hdGNoIHRoZSBwcm9qZWN0IHByb2plY3Rpb24Kd3RyX3NmIDwtICAKICAgICAgICB3dHJfc3AgJT4lIAogICAgICAgIHN0X2FzX3NmKCkgJT4lIAogICAgICAgIG1pc2NnaXM6OmNvZXJjZV90b19nZW9tKHN0X211bHRpcG9seWdvbikKCnd0cl9zZiAlPiUgCiAgICAgICAgc3RfaW50ZXJzZWN0cyh4ID0gc2VhX2NjZF9zZix5ID0gLikgJT4lIAogICAgICAgIHVubGlzdCh1c2UubmFtZXMgPSBGKSAlPiUgCiAgICAgICAgd3RyX3NmWy4sXSAlPiUgCiAgICAgICAgd3JpdGVfcmRzKHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS93dHItc2YucmRzJykpCn0KCnd0cl9zZiA8LSByZWFkX3Jkcyhyb290X2ZpbGUoJzEtZGF0YS80LWludGVyaW0vd3RyLXNmLnJkcycpKQoKc2hvd193dHIgPC0gZnVuY3Rpb24oKXsKICAgICAgICBteUxmbHRHcmV5KGRhdGEgPSBhcyh3dHJfc2YsJ1NwYXRpYWwnKSkgJT4lIAogICAgICAgICAgICAgICAgbXlMZmx0T3B0cygpICU+JSAKICAgICAgICAgICAgICAgIGFkZFBvbHlnb25zKGNvbG9yID0gYmx1ZSwgb3BhY2l0eSA9IDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gLjUsIGZpbGxDb2xvciA9IGJsdWUsZmlsbE9wYWNpdHkgPSAuNSkgICAgICAgIAp9CgpzaG93X3d0cigpCgpgYGAKCiMjIyBLQyBUcmFjdHMgV2l0aG91dCAoV2VzdGVybikgV2F0ZXJib2RpZXMgey19CmBgYHtyIG1pc2MtdHJhY3RzLWNjZC1uby13dHIsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTIsIGZpZy5zaG93PSdob2xkJywgZHBpPTE1MH0KCmlmKCFmaWxlLmV4aXN0cyhyb290X2ZpbGUoJzEtZGF0YS80LWludGVyaW0vdHIta2MtMjAxNS1zZi5yZHMnKSkpewogICAgICAgIHRyX2tjX3d0cl8yMDE1X3NmICU+JSAKICAgICAgICAgICAgICAgIGZpbHRlcihUUkFDVENFICUhaW4lICc5OTAxMDAnKSAlPiUgIyByZW1vdmUgdGhlIFB1Z2V0IFNvdW5kIHRyYWN0CiAgICAgICAgICAgICAgICBtdXRhdGUoZ2VvbWV0cnkgPSBzdF9kaWZmZXJlbmNlKGdlb21ldHJ5LHN0X3VuaW9uKHd0cl9zZikpKSAlPiUgCiAgICAgICAgICAgICAgICBjb2VyY2VfdG9fZ2VvbShzdF9tdWx0aXBvbHlnb24pICU+JSAKICAgICAgICAgICAgICAgIHdyaXRlX3Jkcyhyb290X2ZpbGUoJzEtZGF0YS80LWludGVyaW0vdHIta2MtMjAxNS1zZi5yZHMnKSkKfQoKaWYoIWZpbGUuZXhpc3RzKHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS90ci1rYy0yMDA5LXNmLnJkcycpKSl7CiAgICAgICAgdHJfa2Nfd3RyXzIwMDlfc2YgJT4lIAogICAgICAgICAgICAgICAgZmlsdGVyKFRSQUNUQ0UwMCAlIWluJSAnOTkwMTAwJykgJT4lICMgcmVtb3ZlIHRoZSBQdWdldCBTb3VuZCB0cmFjdAogICAgICAgICAgICAgICAgbXV0YXRlKGdlb21ldHJ5ID0gc3RfZGlmZmVyZW5jZShnZW9tZXRyeSxzdF91bmlvbih3dHJfc2YpKSkgJT4lIAogICAgICAgICAgICAgICAgY29lcmNlX3RvX2dlb20oc3RfbXVsdGlwb2x5Z29uKSAlPiUgCiAgICAgICAgICAgICAgICB3cml0ZV9yZHMocm9vdF9maWxlKCcxLWRhdGEvNC1pbnRlcmltL3RyLWtjLTIwMDktc2YucmRzJykpCn0KCgp0cl9rY18yMDE1X3NmIDwtIHJlYWRfcmRzKHJvb3RfZmlsZSgnMS1kYXRhLzQtaW50ZXJpbS90ci1rYy0yMDE1LXNmLnJkcycpKQoKdHJfa2NfMjAwOV9zZiA8LSByZWFkX3Jkcyhyb290X2ZpbGUoJzEtZGF0YS80LWludGVyaW0vdHIta2MtMjAwOS1zZi5yZHMnKSkKCnNob3dfdHJfa2NfMjAwOV9zZiA8LSBmdW5jdGlvbigpewogICAgICAgIAogICAgICAgIGJsdWVfb3JhbmdlIDwtIGMoYmx1ZSxvcmFuZ2UpICU+JSB1bmxpc3QKICAgICAgICAKICAgICAgICBwYWwgPC0gY29sb3JGYWN0b3IoYmx1ZV9vcmFuZ2UsbGV2ZWxzID0gYyhUUlVFLEZBTFNFKSwgb3JkZXJlZCA9IFRSVUUpCiAgICAgICAgCiAgICAgICAgbXlMZmx0R3JleSgpICU+JSAKICAgICAgICAgICAgICAgIG15TGZsdE9wdHMoKSAlPiUgCiAgICAgICAgICAgICAgICBhZGRQb2x5Z29ucyhkYXRhID0gYXModHJfa2NfMjAwOV9zZiwiU3BhdGlhbCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH4gcGFsKFNFQUNDRF9MR0wpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+IHBhbChTRUFDQ0RfTEdMKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjUpICU+JSAKICAgICAgICAgICAgICAgIGFkZExlZ2VuZChwb3NpdGlvbiA9ICdib3R0b21yaWdodCcsY29sb3JzID0gYmx1ZV9vcmFuZ2UsbGFiZWxzID0gYygnU2VhdHRsZSBDQ0QnLCdPdGhlciBUcmFjdHMnKSx0aXRsZSA9ICdwcmUtMjAxMCcpICU+JSAKICAgICAgICAgICAgICAgIG1pc2NnaXM6OnN0eWxlV2lkZ2V0KHN0eWxlID0gImZsb2F0OmxlZnQ7bWFyZ2luOjFweCIpCn0KCnNob3dfdHJfa2NfMjAxNV9zZiA8LSBmdW5jdGlvbigpewogICAgICAgIAogICAgICAgIGJsdWVfb3JhbmdlIDwtIGMoYmx1ZSxvcmFuZ2UpICU+JSB1bmxpc3QKICAgICAgICAKICAgICAgICBwYWwgPC0gY29sb3JGYWN0b3IoYmx1ZV9vcmFuZ2UsbGV2ZWxzID0gYyhUUlVFLEZBTFNFKSwgb3JkZXJlZCA9IFRSVUUpCiAgICAgICAgCiAgICAgICAgbXlMZmx0R3JleSgpICU+JSAKICAgICAgICAgICAgICAgIG15TGZsdE9wdHMoKSAlPiUgCiAgICAgICAgICAgICAgICBhZGRQb2x5Z29ucyhkYXRhID0gYXModHJfa2NfMjAxNV9zZiwiU3BhdGlhbCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH4gcGFsKFNFQUNDRF9MR0wpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+IHBhbChTRUFDQ0RfTEdMKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjUpICU+JSAKICAgICAgICAgICAgICAgIGFkZExlZ2VuZChwb3NpdGlvbiA9ICdib3R0b21yaWdodCcsY29sb3JzID0gYmx1ZV9vcmFuZ2UsbGFiZWxzID0gYygnU2VhdHRsZSBDQ0QnLCdPdGhlciBUcmFjdHMnKSx0aXRsZSA9ICcyMDEwJykgJT4lIAogICAgICAgICAgICAgICAgbWlzY2dpczo6c3R5bGVXaWRnZXQoc3R5bGUgPSAiZmxvYXQ6bm9uZTttYXJnaW46MXB4IikKfQoKc2hvd190cl9rY18yMDA5X3NmKCkKCnNob3dfdHJfa2NfMjAxNV9zZigpCgoKCmBgYAoK